SQLite y DuckDB comparten algo llamativo: las dos son bases de datos embebidas — una librería que vive en tu proceso, sin servidor separado. Y sin embargo resuelven problemas distintos. SQLite es el rey de transacciones pequeñas y persistencia local; DuckDB es el rey del análisis columnar rápido sin infra. Elegir mal entre ellas es un error con coste.
Este artículo es una comparación honesta, con criterios para decidir y escenarios donde cada una gana — y cuándo se pueden (deben) usar juntas.
El punto en común: embedded
Las dos descartan el modelo cliente-servidor. Tu aplicación importa la librería y ejecuta consultas SQL sobre un archivo. No hay systemd, ni puertos, ni usuarios, ni replicación como aspecto central. Esto reduce brutalmente la complejidad operativa.
- SQLite: un archivo
.db(más algunos WAL temporales). Dumps, copias, migraciones soncp. - DuckDB: un archivo
.duckdb(o en memoria, o Parquet directamente).
Este patrón embedded es perfecto para: apps móviles, desktop, servicios de una sola instancia, notebooks analíticos, pipelines ETL locales. Es el anti-patrón para: sistemas con decenas de servicios concurrentes contra la misma BD.
Diferencia estructural: filas vs columnas
La separación no es cosmética. Es de arquitectura:
- SQLite guarda datos por filas. Cada fila es un bloque contiguo. Perfecto para:
SELECT * FROM users WHERE id = 42— leer una fila entera rápido. - DuckDB guarda datos por columnas. Cada columna es un bloque contiguo. Perfecto para:
SELECT AVG(amount) FROM transactions WHERE year=2023— leer pocos campos de muchas filas.
OLTP vs OLAP, en formato embedded.
Cuándo elegir SQLite
Escenarios donde SQLite es claramente correcto:
- App móvil con BD local: iOS, Android, desktop. SQLite es el estándar de facto.
- Configuración y persistencia de una app: Firefox, Chrome, decenas de apps lo usan.
- Servicio web con un solo nodo y lectura/escritura mixta moderada.
- Prototipo o MVP antes de migrar a PostgreSQL.
- Backup durable y transaccional local con ACID completo.
- WASM en el navegador via sql.js o similar.
SQLite hace bien lo que debe hacer: transacciones pequeñas, alta integridad, baja latencia por operación.
Cuándo elegir DuckDB
Escenarios donde DuckDB brilla:
- Análisis de logs: millones de filas, queries agregados.
- Consultar archivos Parquet/CSV directamente sin importar.
SELECT * FROM 'datos.parquet'funciona. - Reemplazar pandas en pipelines medianos (memoria eficiente, SQL nativo).
- Análisis ad-hoc en notebooks: Jupyter + DuckDB es potente.
- Consolidar bases OLTP para reports sin tocar producción (CDC + DuckDB).
- Procesamiento vectorizado sobre datos tabulares sin infraestructura distribuida.
DuckDB es lo que Pandas-con-SQL-nativo debería haber sido.
Benchmarks orientativos
Con los debidos asteriscos (benchmarks de BD son mentirosos sin contexto), órdenes de magnitud:
- Insert individual de 1M filas: SQLite ~20s (batched). DuckDB: tarda más — no es su caso.
- Aggregate sobre 1M filas con GROUP BY: SQLite ~15s. DuckDB ~1s.
- Scan de 100M filas columnares con filtro selectivo: SQLite memoria lenta. DuckDB segundos.
- Transacciones concurrentes: SQLite (con WAL) ~1000 tx/s. DuckDB no está diseñado para esto.
Cada uno es 10x-100x más rápido que el otro en el terreno correcto.
Integración en Python
Ambas son trivial de usar desde Python:
# SQLite
import sqlite3
con = sqlite3.connect('app.db')
con.execute("CREATE TABLE users (id INT, name TEXT)")
# DuckDB
import duckdb
con = duckdb.connect('analytics.duckdb')
# Consultar Parquet directamente
df = con.execute("SELECT * FROM 'events.parquet' WHERE type='signup'").df()
DuckDB tiene integración Python especialmente pulida: DataFrames de pandas y Polars se consumen/producen sin overhead serio.
Usarlas juntas
Combinarlas es patrón común:
- OLTP en SQLite, OLAP en DuckDB. La app escribe en SQLite; un job periódico vuelca cambios a DuckDB/Parquet para análisis.
- DuckDB lee SQLite directamente con el extension
sqlite_scanner.SELECT * FROM sqlite_scan('app.db', 'users'). Sin ETL. - Archivar datos antiguos de SQLite a Parquet. DuckDB mantiene queries sobre históricos sin inflar la BD de producción.
El patrón “SQLite para caliente + DuckDB para frío” es muy productivo.
Limitaciones reales
Seamos honestos:
SQLite no es para:
- Escrituras concurrentes altas — incluso con WAL, el write lock es global.
- Múltiples procesos escribiendo — hay race conditions sutiles.
- BDs gigantes (>100GB) — técnicamente posible, operacionalmente incómodo.
- Replicación built-in — herramientas como Litestream ayudan.
DuckDB no es para:
- OLTP real — no hay locking fino, concurrencia pensada para análisis.
- Latencia sub-milisegundo — es rápido pero optimizado para queries analíticos.
- BDs con muchas writes concurrentes — falla al caso.
Intentar que cada una haga lo del otro es hacerse daño.
El veredicto
Cómo decidir en 10 segundos:
- ¿Transacciones cortas, alta integridad, un proceso activo? SQLite.
- ¿Queries analíticos sobre grandes volúmenes, pocas writes? DuckDB.
- ¿App móvil / desktop / servicio simple? SQLite.
- ¿Notebooks, ETL local, reemplazar pandas? DuckDB.
- ¿Ambas cosas? Usarlas juntas.
Cualquier duda honesta se resuelve mirando la query principal: si es un WHERE id=? casi siempre, SQLite. Si es un GROUP BY sobre mucha fila, DuckDB.
Conclusión
SQLite y DuckDB son herramientas complementarias, no competidoras. Ambas representan la filosofía “embedded importa” aplicada a diferentes problemas. Para OLTP local, SQLite es imbatible por simplicidad y robustez. Para OLAP sin infraestructura, DuckDB ha hecho obsoletas muchas soluciones complejas. Conocer las dos y aplicar la correcta es una ventaja técnica real — y combinarlas cuando tiene sentido multiplica el valor de ambas.
Síguenos en jacar.es para más sobre bases de datos, arquitecturas de datos y herramientas embebidas.